﻿// copyright jyz.2024


#include "TargetDataUnderMouse.h"

#include "AbilitySystemComponent.h"

UTargetDataUnderMouse* UTargetDataUnderMouse::CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility)
{
	UTargetDataUnderMouse* MyObj = NewAbilityTask<UTargetDataUnderMouse>(OwningAbility);
	return MyObj;
}

void UTargetDataUnderMouse::Activate()
{
	const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
	if(bIsLocallyControlled)
	{
		//Client
		SendMouseCursorData();
	}
	else
	{
		//Todo:We are on the server,so listen for target data（我们在服务器上，所以监听目标数据）
		//Server
		const FGameplayAbilitySpecHandle SpecHandle = GetAbilitySpecHandle();
		const FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();
		AbilitySystemComponent->AbilityTargetDataSetDelegate(
			SpecHandle,
			ActivationPredictionKey).AddUObject(this,&UTargetDataUnderMouse::OnTargetDataReplicatedCallback);
		const bool bCalledDelegate = AbilitySystemComponent->CallReplicatedTargetDataDelegatesIfSet(SpecHandle,ActivationPredictionKey);
		//如果没有调用，等待远程同步
		if(!bCalledDelegate)
		{
			SetWaitingOnRemotePlayerData();
		}
	}
}

void UTargetDataUnderMouse::SendMouseCursorData() const
{
	//创建一个预测窗口的实例。预测窗口用于网络同步，记录和处理网络预测行为，
	//比如技能激活预测，以减轻网络延迟的影响。这个预测窗口会在当前代码块或函数作用域结束时自动关闭。
	FScopedPredictionWindow PredictionWindow(AbilitySystemComponent.Get());
	
	APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
	FHitResult CursorHit;
	PC->GetHitResultUnderCursor(ECC_Visibility,false,CursorHit);
	FGameplayAbilityTargetDataHandle DataHandle;
	FGameplayAbilityTargetData_SingleTargetHit*SingleTargetHit = new FGameplayAbilityTargetData_SingleTargetHit();
	SingleTargetHit->HitResult = CursorHit;
	DataHandle.Add(SingleTargetHit);

	//Todo:GetActivationPredictionKey()：
	//这是一个在技能激活开始时创建的唯一键，
	//用于跟踪和校验预测的技能激活过程。它是在客户端初始激活技能时生成的，
	//然后随着技能激活的细节一起发送到服务器。
	//如果客户端预测的技能激活被服务器确认，或者如果预测失败并需要纠正，那么这个键就被用来识别出它。
	
	//Todo:AbilitySystemComponent->ScopedPredictionKey：
	//这是当前作用域（scope）的预测键。
	//作用域主要是关联所有在相同网络更新（replication packet）中的预测事件。
	//服务器在接收到包含多个预测事件的更新时，可以使用作用域预测键来识别并正确处理这些事件。

	//总的来说，这两个预测键在预测技能激活和处理网络同步中起到了关键作用。
	AbilitySystemComponent->ServerSetReplicatedTargetData(
		GetAbilitySpecHandle(),
		GetActivationPredictionKey(),
		DataHandle,
		FGameplayTag(),
		AbilitySystemComponent->ScopedPredictionKey
		);

	if(ShouldBroadcastAbilityTaskDelegates())
	{
		ValidData.Broadcast(DataHandle);
	}
}

void UTargetDataUnderMouse::OnTargetDataReplicatedCallback(
	const FGameplayAbilityTargetDataHandle& DataHandle,
	FGameplayTag GameplayTag) const
{
	//数据已经收到，不要存储它（释放掉这个数据）
	AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(),GetActivationPredictionKey());
	if(ShouldBroadcastAbilityTaskDelegates())
	{
		ValidData.Broadcast(DataHandle);
	}
}
